<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>TwinNode.js</title>
  <style>
    :root {
      --color: red;
    }

    table {
      border: 1px solid gray;
      border-collapse: collapse;
      width: 400px;
      margin-top: 10px;

      & th,
      td {
        border: 1px solid gray;
        padding: 5px 10px;
      }
    }

    .box {
      border: 1px solid gray;
      background-color: #fff;
    }

    .box * {
      border: 1px solid gray;
    }

    #var {
      color: var(--color);
      font-size: 20px;
      ;
    }
  </style>
  <script src="twin-node.js"></script>
</head>

<body>
  <div id="container1"></div>
  <div id="container2"></div>
  <div id="container3"></div>
  <div id="container4"></div>
  <div id="container5"></div>
  <div id="container6"></div>
  <div id="container7"></div>
  <div id="container8"></div>
  <div id="container9"></div>
  <div id="container10"></div>
  <div id="container11"></div>
  <div id="absoluteContainer" class="box">
    <div class="top">top</div>
    <div class="left">left</div>
    <div class="main">main</div>
    <div class="right">right</div>
    <div class="bottom">bottom</div>
  </div>
  <div id="flexContainer" class="box" style="height: 50px;">
    <div></div>
    <div></div>
    <div></div>
  </div>
  <div id="gridContainer" style="height: 600px;margin-top: 10px;" class="box">
    <div class="top"></div>
    <div class="left"></div>
    <div class="center"></div>
    <div class="right"></div>
    <div class="bottom"></div>
  </div>
  <script>
    /*********************标签函数与孪生节点***********************
      标签函数用来申明式构建DOM节点，这种节点我们称为孪生节点
      请采用推荐的解构语法从TwinNode.tags中获取标签函数
          1: 标签函数名与想要创建的DOM节点的标签同名
          2: 下划线是一个特殊的标签函数，用于创建文档碎片
          3: 用下划线语法来声明自定义标签，my_div对应的是my-div标签
      TwinNode.tags实质上是一个Proxy，能够兼容过去未来所有的合法标签 
    *************************************************************/
    const { _, style, div, ul, li, label, input, table, thead, tbody, tr, th, td } = TwinNode.tags

    const body = $$.q("body");


    /************静态构建**********
      1：html语法与js语法的完美融合
      2：无需借助任何编译工具
    ******************************/
    const node1 = table(
      thead(
        tr(
          th("字段1"), th("字段2"), th("字段3")
        )
      ),
      tbody(
        tr(
          td("内容"), td("内容"), td("内容")
        ),
        tr(
          td("内容"), td("内容"), td("内容")
        )
      ),
    )
    //挂载节点
    node1.appendTo($$.q("#container1"))

    /***********无参的函数组件********************
      组件就是一个有返回值的函数
      符合js语法即可，除此之外没有任何要求
    ********************************************/
    function component2() {
      return tbody(
        tr(
          td("内容"), td("内容"), td("内容")
        ),
        tr(
          td("内容"), td("内容"), td("内容")
        )
      )
    }
    //使用组件
    const node2 = table(
      thead(
        tr(
          th("字段1"), th("字段2"), th("字段3")
        )
      ),
      component2   //等价与component2()，框架会自动调用无参的函数组件
    )
    //渲染节点
    node2.appendTo($$.q("#container2"))



    /***********有参的函数组件***********/
    //定义组件
    function component3(data) {
      return tbody(
        data.map(item =>
          tr(
            td(item.id),
            td(item.name),
            td(item.sex === 1 ? "男" : "女")
          )
        )
      )
    }
    //准备数据
    const myFamily = [
      { id: 1, name: "张向明", sex: 1 },
      { id: 2, name: "蒋海艳", sex: 2 },
      { id: 3, name: "张泽珩", sex: 1 }
    ]
    //使用组件
    const node3 = table(
      thead(
        tr(
          th("字段1"), th("字段2"), th("字段3")
        )
      ),
      component3(myFamily)  //调用组件函数并传入参数
    )
    //渲染节点
    node3.appendTo($$.q("#container3"))





    /***********文档碎片组件*******************************
      有些组件可能没有父节点
      此时可以用_标签函数来构建一个文档碎片(DocumentFragment)
      _()类似与React的<><>
    *******************************************************/
    function component4() {
      return _(
        li("项目10"),
        li("项目11"),
        li("项目12")
      )
    }
    const node4 = ul(
      component4
    )
    node4.appendTo($$.q("#container4"))





    /***********声明式配置属性、样式、事件***********
      标签函数接收如下与顺序无关的参数类型
        1: 空值，将被忽略，0不是空值
        2: 真实的Node实例，直接追加到当前真实node
        3: 数组，会自动展开，每一个子项将被递归追加到当前节点
        4: 孪生节点，表示在当前节点追加子节点
        5: 函数，会被自动调用，返回值将被递归追加到当前节点 
        6：特殊正则表达式，为当前节点配置id和class
          6.1 #开始的片段将被配置为id,多个id取最后一个
          6.2 .开始的片段将被配置为className，多个className可叠加
        7：Object，表示为当前节点配置attribute
          2.1 key为字符串value为函数，表示为当前节点配置事件
          2.2 key为"style"，value为Object，表示配置样式
          2.3 其他情况皆为配置简单attribute
        8：其他情况全部当成字符串追加到当前节点
         
    ********************************************/
    const node5 = ul(

      //1：以下空值将被忽略
      null, undefined, "",

      //2：追加真实节点
      document.createElement("li"),

      //3：数组会自动展开，如果你觉得别扭，也可以用...来手动展开
      [li("项目1"), li("项目2")],

      //4：孪生节点
      li("项目3"),

      //5：函数会自动运行并将结果递归追加到当前节点
      () => li(`项目4`),

      //6: 特殊正则配置id和class
      /#ul5.class1.class2/,

      //7：配置attribute,样式、事件
      {
        title: "标题",  //配置属性
        style: { color: "red", fontSize: "20px" },  //value为object，被判定为配置样式
        click: event => console.log(event.target)   //value为Function，被判定为配置事件
      }

    )
    node5.appendTo($$.q("#container5"))





    /***********属性、样式、事件后置***********
      如果节点嵌套得很深，用object的方式来配置属性会使结构变得难以维护
      此时，你可以在标签函数调用后用链式写法来配置属性
        1: attr(name,value)用来配置attribute
        2: css(name,value)用来配置样式
        3：on(eventType,...args)用来配置事件
        4: class(className)用来配置class
    ***********/
    const node6 = ul(
      li("我有id颜色").attr("id", "project16").css("color", "red"),
      li("class1").class("class1"),
      li("点我").on("click", event => alert(event.target))
    )
    node6.appendTo($$.q("#container6"))





    /***********与svg的集成***********
      从TwinNode.svgTags解构标签函数
    **********************************/
    const { svg, path } = TwinNode.svgTags;
    const node7 = svg(
      { width: 100, height: 100 },
      path(
        { d: "M0,0 L100,100", stroke: "red", "stroke-width": 1 }
      )
    )
    node7.appendTo($$.q("#container7"))





    /***********与web component的集成***********/
    customElements.define(
      "my-div",
      class MyDiv extends HTMLElement {
        constructor() {
          super();
          this.attachShadow({ mode: "open" });
          this.shadowRoot.innerHTML = `
          <style>
            :host{
              --color:green;
            }
          </style>
          <h1 style="color:var(--color)">这是一个webcomponent组件</h1><slot></slot>`;
        }
      }
    )
    const { my_div } = TwinNode.tags;  //解构webcomponent标签函数，注意my_div的下划线对应是my-div的中划线
    const node8 = div(
      my_div("这是组件的内容")
    )
    node8.appendTo($$.q("#container8"))




    /***************直接使用TwinNode***************
      如果你这样做，一定会被项目经理打死
      我这样做只是为了让你看清TwinNode的本质
    **************************************************/
    TwinNode("ul").append(
      TwinNode("li").append("张三",).append({ id: "li1", class: "list-item", style: { color: "red" } }),
      TwinNode("li").append("李四").append({ id: "li2", class: "list-item", style: { color: "red" } }),
      TwinNode("li").append("王五").append({ id: "li3", class: "list-item", style: { color: "red" } })
    )
      .appendTo($$.q("#container9"))




    /*****************致敬jquery****************
      TwinNode.js除了提供高性能的原生构建能力外，还有如下特性
      1：类似于jquery的精确而优美的控制能力
      2：链式调用
      3：动态代理函数兼容现在和未来所有的属性、样式、事件配置       
    *******************************************/
    const container10 = document.querySelector("#container10")
    //$$(node)函数，可为该原生Node创建一个孪生节点
    const twinNode = $$(container10)     //等价与TwinNode(container10)
    twinNode.append(     //用append追加内容
      div(/#div1/),
      div(),
      div()
    )
      .attr("title", "提示")   //用attr配置attribute
      .class("class1")        //用class配置className
      .css("color", "red")     //用css配置style
      .on("click", event => console.log(event.target)) //用on配置event
    //孪生节点通过动态代理函数来配置属性、样式、事件
    //孪生节点实质上是一个Proxy，能够识别过去、现在、未来所有的属性、样式和事件
    twinNode
      ._id("newid")                 //配置属性，属性名前加下滑线
      .width("auto")                //配置样式，函数名既是样式名      
      .Mousedown(event => {         //配置事件，首字母大写
        console.log(event.target)
      })
    //游标函数
    $$(container10)             //为原生Node创建孪生节点
      .q("#div1").html("div1")  //用id选择器找到div1,并设置innerHTML
      .next().text("div2")      //用next找到div2,并设置innerText
      .next().text("div3")      //用next找到div3,并设置innerText
      .previous().color("red")  //用prvious返回div2,并设置样式
      .parent()                 //返回父节点





    /***************内置的布局函数***************
      dock(where,size,offset)
        将节点停靠在窗口的指定位置，使其不受滚动条影响
        where:停靠的位置，可选值：top,right,bottom,left,center
        size:浮窗的调好大小
        offset:先对于停靠位置的偏移量
      absolute(top,right,bottom,left,width,height)
        将节点绝对定位，并配置位置和大小，位置和大小要搭配使用
      flexCake(direction,...grows)
        将节点flex布局
        dirction:flex主轴方向，可选值为row和column
        ...grows:子项获得的蛋糕份数
      
    **************************************************/

    //向body中插入一个停靠的浮窗,偏移10像素
    div("内容")
      .dock("top", 200, 10)     //将div停靠在窗口的顶部，高度为200像素，偏移10像素
      .background("lightgray")
      .opacity("0.8")
      .appendTo(body);

    //将容器配置为绝对单位，并对内容进行绝对定位布局
    //参数的顺序为top,right,bottom,left,width,height
    $$.q("#absoluteContainer")
      .absolute(200, 200, 200, 200, null, null)   //绝对定位，四边位置为200，高宽为auto
      .absoluteChild(
        {
          ".top": [0, 0, null, 0, null, 30],
          ".left": [30, null, 30, 0, 100, null],
          ".main": [30, 100, 30, 100, null, null],
          ".right": [30, 0, 30, null, 100, null],
          ".bottom": [null, 0, 0, 0, null, 30, null]
        }
      )

    //容器启用flex布局，总12分，第一列分3份，第二列分6份，第三列分3份
    $$.q("#flexContainer").flexCake("row", 3, 6, 3)

    //grid布局
    //参数1：总行数
    //参数2：总列数
    //参数3：要控制的子项的选择器的rowspan和colspan
    $$.q("#gridContainer")
      .grid(6, 6,
        {
          ".top": [1, 6],
          ".left": [4, 1],
          ".center": [4, 4],
          ".right": [4, 1],
          ".bottom": [1, 6]
        }
      )


  </script>
</body>

</html>